import { ComponentPublicInstance } from 'vue';

/* eslint-disable @typescript-eslint/no-explicit-any */
export function getNestedValue(obj: any, path: (string | number)[], fallback?: any): any {
  const last = path.length - 1;

  if (last < 0) return obj === undefined ? fallback : obj;

  for (let i = 0; i < last; i++) {
    if (obj == null) {
      return fallback;
    }
    obj = obj[path[i]];
  }

  if (obj == null) return fallback;

  return obj[path[last]] === undefined ? fallback : obj[path[last]];
}

export function deepEqual(a: any, b: any): boolean {
  if (a === b) return true;

  if (a instanceof Date && b instanceof Date && a.getTime() !== b.getTime()) {
    // If the values are Date, compare them as timestamps
    return false;
  }

  if (a !== Object(a) || b !== Object(b)) {
    // If the values aren't objects, they were already checked for equality
    return false;
  }

  const props = Object.keys(a);

  if (props.length !== Object.keys(b).length) {
    // Different number of props, don't bother to check
    return false;
  }

  return props.every((p) => deepEqual(a[p], b[p]));
}

export function getObjectValueByPath(obj: any, path: string, fallback?: any): any {
  // credit: http://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key#comment55278413_6491621
  if (obj == null || !path || typeof path !== 'string') return fallback;
  if (obj[path] !== undefined) return obj[path];
  path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
  path = path.replace(/^\./, ''); // strip a leading dot
  return getNestedValue(obj, path.split('.'), fallback);
}

export type SelectItemKey =
  | string // Lookup by key, can use dot notation for nested objects
  | (string | number)[] // Nested lookup by key, each array item is a key in the next level
  | ((item: Record<string, any>, fallback?: any) => any);

export function getPropertyFromItem(item: any, property: SelectItemKey, fallback?: any): any {
  if (property == null) return item === undefined ? fallback : item;

  if (item !== Object(item)) {
    if (typeof property !== 'function') return fallback;

    const value = property(item, fallback);

    return typeof value === 'undefined' ? fallback : value;
  }

  if (typeof property === 'string') return getObjectValueByPath(item, property, fallback);

  if (Array.isArray(property)) return getNestedValue(item, property, fallback);

  if (typeof property !== 'function') return fallback;

  const value = property(item, fallback);

  return typeof value === 'undefined' ? fallback : value;
}

export function convertToUnit(str: string | number | null | undefined, unit = 'px'): string | undefined {
  if (str == null || str === '') {
    return undefined;
  } else if (isNaN(+str!)) {
    return String(str);
  } else if (!isFinite(+str!)) {
    return undefined;
  } else {
    return `${Number(str)}${unit}`;
  }
}

export function isObject(obj: any): obj is object {
  return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
}

export function isComponentInstance(obj: any): obj is ComponentPublicInstance {
  return obj?.$el;
}

/**
 * 是否外部链接地址
 */
export function isExternal(path: string) {
  const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
  return isExternal;
}

export function accDiv(arg1: number, arg2: number) {
  let t1 = 0,
    t2 = 0;

  try {
    t1 = arg1.toString().split('.')[1].length;
    t2 = arg2.toString().split('.')[1].length;
  } catch (e) {
    /* empty */
  }

  const r1 = Number(arg1.toString().replace('.', '')); //--去除小数点变整数
  const r2 = Number(arg2.toString().replace('.', '')); //--去除小数点变整数
  return (r1 / r2) * Math.pow(10, t2 - t1); //---整数相除 在乘上10的平方  小数点的长度
}

export function accMul(arg1: number, arg2: number) {
  let m = 0;
  const s1 = arg1.toString();
  const s2 = arg2.toString();

  try {
    m += s1.split('.')[1].length;
    m += s2.split('.')[1].length;
  } catch (e) {
    /* empty */
  }

  return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m);
}

export function accAdd(arg1: number, arg2: number) {
  let r1, r2;

  try {
    r1 = arg1.toString().split('.')[1].length;
  } catch (e) {
    r1 = 0;
  }

  try {
    r2 = arg2.toString().split('.')[1].length;
  } catch (e) {
    r2 = 0;
  }

  const m = Math.pow(10, Math.max(r1, r2));

  return (arg1 * m + arg2 * m) / m;
}

//减法函数
export function accSub(arg1: number, arg2: number) {
  let r1, r2;

  try {
    r1 = arg1.toString().split('.')[1].length;
  } catch (e) {
    r1 = 0;
  }

  try {
    r2 = arg2.toString().split('.')[1].length;
  } catch (e) {
    r2 = 0;
  }

  const m = Math.pow(10, Math.max(r1, r2));

  const n = r1 >= r2 ? r1 : r2;

  return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
